package com.ssm.changeunit.command;

import com.ssm.changeunit.Branch;
import com.ssm.changeunit.BranchFactory;
import com.ssm.changeunit.ChangeLog;
import com.ssm.changeunit.ChangeUnit;
import com.ssm.changeunit.ChangeUnitException;
import com.ssm.changeunit.ChangeUnitFactory;
import com.ssm.changeunit.ChangeUnitItem;
import com.ssm.changeunit.Environment;
import com.ssm.changeunit.ManagedItemListFactory;
import com.ssm.changeunit.communicate.ChangeUnitClient;
import com.ssm.changeunit.communicate.ChangeUnitRPCResult;
import com.ssm.changeunit.diff.ContentMerger;
import com.ssm.changeunit.persistance.ChangeLogDAO;
import com.ssm.changeunit.persistance.ChangeLogDTO;
import com.ssm.changeunit.persistance.ChangeUnitDAO;
import com.ssm.changeunit.persistance.ChangeUnitDTO;
import com.ssm.changeunit.persistance.ChangeUnitItemDAO;
import com.ssm.changeunit.persistance.ChangeUnitItemDTO;
import com.ssm.changeunit.persistance.ChildDAO;
import com.ssm.changeunit.persistance.ChildDTO;
import com.ssm.changeunit.persistance.DatabaseManager;
import com.ssm.changeunit.persistance.ManagedChangeUnitDAO;
import com.ssm.changeunit.persistance.ManagedChangeUnitDTO;
import com.ssm.changeunit.persistance.ManagedItemListDAO;
import com.ssm.changeunit.persistance.ManagedItemListDTO;
import com.ssm.changeunit.persistance.ParentDAO;
import com.ssm.changeunit.persistance.ParentDTO;
import com.ssm.changeunit.persistance.PersistanceException;
import com.ssm.changeunit.util.ChangeUnitTraveler;
import com.ssm.changeunit.util.FileUtil;
import java.io.IOException;
import java.util.List;
import java.util.logging.Level;
import org.apache.log4j.Logger;

/**
 * 
 * @author 시리우스
 */
public class PullCommandWorker extends CommandWorker {
    private Logger log = Logger.getLogger(PullCommandWorker.class);

    public PullCommandWorker () {
    }

    public void work (CommandWorkerOptions commandWorkerOptions) throws CommandWorkerException {
        if( commandWorkerOptions == null )
            throw new CommandWorkerException("Option이 null입니다.");
        
        if( !commandWorkerOptions.hasCommandWorkerOption("server") )
            throw new CommandWorkerException("server Option이 존재하지 않습니다.");
        
        // Item option을 꺼내옵니다.
        CommandWorkerOption serverCommandWorkerOption = commandWorkerOptions.getCommandWorkerOption("server");
        
        // Item option에 해당하는 item들을 가져옵니다.
        String[] servers = serverCommandWorkerOption.getValues();
        
        // item의 값이 없는 경우
        if( servers.length == 0 )
            throw new CommandWorkerException("Server의 값이 존재하지 않습니다.");
        
        // 데이터베이스 체크
        log.debug("데이터베이스가 존재하는지 검사합니다.");
        
        try {
            if(!DatabaseManager.getInstance().isExists())
                throw new CommandWorkerException("데이터베이스가 존재하지 않습니다.");
        }
        catch(PersistanceException ex) {
            throw new CommandWorkerException(ex);
        }
        
        String branchName = "";
        
        try {
            branchName = Environment.getCurrentBranch();
        } 
        catch (IOException ex) {
            throw new CommandWorkerException(ex);
        }
        
        try {
            ChangeUnitClient changeUnitClient = new ChangeUnitClient(servers[0]);
            
            // 서버의 최신 ChangeUnitId를 가져옵니다.
            String serverCurrentChangeUnitId = changeUnitClient.getCurrentChangeUnitIdOfBranch(branchName);

            log.debug("서버의 ChangeUnitId: " + serverCurrentChangeUnitId);
            
            ChangeUnitDAO changeUnitDAO = new ChangeUnitDAO();

            if( changeUnitDAO.find(serverCurrentChangeUnitId) != null )
                throw new CommandWorkerException("Pull을 할 필요가 없습니다.");
            
            ChangeUnit equalChangeUnit = null;
            
            // 서버의 끝점을 찾기위해 ChangeUnitTraveler를 구한다.
            ChangeUnitTraveler changeUnitTraveler = new ChangeUnitTraveler();
            
            changeUnitTraveler.init();
            
            while(changeUnitTraveler.hasNext()) {
                // 하나씩 꺼내서 서버에 ChangeUnitId가 있는지 물어본다.
                ChangeUnit nextChangeUnit = changeUnitTraveler.getNextChangeUnit();
                
                // 서버에 요청
                if(changeUnitClient.isChangeUnitExists(Environment.getCurrentBranch(), nextChangeUnit.getChangeUnitId())) {
                    equalChangeUnit = nextChangeUnit;
                    
                    break;
                }
            }
            
            // 서버에 시작점과 끝점을 줘서 ChangeUnit을 구해온다.
            ChangeUnitRPCResult changeUnitRPCResult = changeUnitClient.getChangeUnitRPCResult(equalChangeUnit.getChangeUnitId(), serverCurrentChangeUnitId, Environment.getCurrentBranch());
            
            log.debug("ChangeUnitRPCResult : " + changeUnitRPCResult);
            
            // ChangeUnit 입력
            List<ChangeUnitDTO> changeUnitDTOs = changeUnitRPCResult.getChangeUnitDTOs();
            
            for(ChangeUnitDTO changeUnitDTO : changeUnitDTOs) {
                if(changeUnitDAO.find(changeUnitDTO.getChangeUnitId()) == null)
                    changeUnitDAO.insert(changeUnitDTO);
            }
            
            // ChangeUnitItem 입력
            List<ChangeUnitItemDTO> changeUnitItemDTOs = changeUnitRPCResult.getChangeUnitItemsDTOs();
            
            ChangeUnitItemDAO changeUnitItemDAO = new ChangeUnitItemDAO();
            
            for(ChangeUnitItemDTO ChangeUnitItemDTO : changeUnitItemDTOs) {
                if(changeUnitItemDAO.find(ChangeUnitItemDTO.getChangeUnitItemId()) == null)
                    changeUnitItemDAO.insert(ChangeUnitItemDTO);
            }
            
            // ChangeLog 입력
            List<ChangeLogDTO> changeLogDTOs = changeUnitRPCResult.getChangeLogDTOs();
            
            ChangeLogDAO changeLogDAO = new ChangeLogDAO();
            
            for(ChangeLogDTO changeLogDTO : changeLogDTOs) {
                if(changeLogDAO.find(changeLogDTO.getChangeLogId()) == null)
                    changeLogDAO.insert(changeLogDTO);
            }
            
            // Parent 입력
            List<ParentDTO> parentDTOs = changeUnitRPCResult.getParentDTOs();
            
            ParentDAO parentDAO = new ParentDAO();
            
            for(ParentDTO parentDTO : parentDTOs) {
                parentDAO.insert(parentDTO);
            }
            
            // Child 입력
            List<ChildDTO> childDTOs = changeUnitRPCResult.getChildDTOs();
            
            ChildDAO childDAO = new ChildDAO();
            
            for(ChildDTO childDTO : childDTOs) {
                childDAO.insert(childDTO);
            }
            
            // 머지한다.
            try {
                // Origin과 dest의 ChangeUnit을 가져옵니다.
                Branch currentBranch = new BranchFactory().createProduct(branchName);
                
                ChangeUnit originChangeUnit = currentBranch.getCurrentChangeUnit();
                ChangeUnit destChangeUnit = new ChangeUnitFactory().createProduct(serverCurrentChangeUnitId);

                // Temp ChangeUnit을 생성한다.
                ChangeUnit tempChangeUnit = new ChangeUnitFactory().createProduct("0000-0000-0000");

                tempChangeUnit.addParent(originChangeUnit);
                tempChangeUnit.addParent(destChangeUnit);

                // CurrentBranch가 Temp ChangeUnit을 가리키도록 한다.
                currentBranch.setCurrentChangeUnit(tempChangeUnit);

                /////////////////////////////////////////////////////////////////////////////////////////////////////////////////
                // Local의 파일 정보를 가져옵니다.
                List<ManagedItemListDTO> managedItemLists = new ManagedItemListDAO().list();
                List<ChangeUnitItem> destChangeUnitItems = destChangeUnit.getChangeUnitItems();

                log.debug("Local에서 관리되는 파일 리스트");

                for(ManagedItemListDTO managedItemListDTO : managedItemLists) {
                    String managedFilePath = managedItemListDTO.getFilePath();

                    log.debug("-> " + managedFilePath);

                    log.debug("Dest에서 " + managedFilePath +"를 찾습니다.");

                    for(ChangeUnitItem changeUnitItem : destChangeUnitItems) {
                        String changeUnitItemFilePath = changeUnitItem.getFilePath();

                        if(managedFilePath.equals(changeUnitItemFilePath)) {
                            log.debug("파일을 찾았습니다.");

                            // 여기서 비교를한 후에..
                            // 파일에 다시 기록해야한다..

                            log.debug("내용을 비교합니다..");

                            ContentMerger contentMerge = new ContentMerger();

                            String originContent = FileUtil.readFromFile(Environment.ROOT_DIR + changeUnitItemFilePath);
                            String targetContent = new String(changeUnitItem.getRefChangeLog().getContent());

                            String[] newContentArray = contentMerge.merge(originContent.split("\n"), targetContent.split("\n"));

                            StringBuilder newContentBuilder = new StringBuilder();

                            for(String line : newContentArray) {
                                log.debug("-> " + line);

                                newContentBuilder.append(line + "\r\n");
                            }

                            // 파일에 기록합니다.
                            log.debug("파일에 기록합니다.");
                            FileUtil.saveDataToFile(changeUnitItemFilePath, newContentBuilder.toString());

                            break;
                        }
                    }
                }

                log.debug("Local에서 관리되는 파일 리스트 끝.");

                ManagedItemListFactory managedItemListFactory = new ManagedItemListFactory();

                // 서버의 새 파일도 가져와서 기록해야한다.
                for(ChangeUnitItem changeUnitItem : destChangeUnitItems) {
                    String changeUnitItemFilePath = changeUnitItem.getFilePath();

                    log.debug("-> " + changeUnitItemFilePath);

                    log.debug("ManagedItemList에서 " + changeUnitItemFilePath +"를 찾습니다.");

                    for(ManagedItemListDTO managedItemListDTO : managedItemLists) {
                        String managedFilePath = managedItemListDTO.getFilePath();

                        if(!managedFilePath.equals(changeUnitItemFilePath)) {
                            log.debug("파일을 찾을 수 없습니다. 새로운 파일입니다.");

                            // 로컬 파일에 기록해야한다.
                            FileUtil.saveDataToFile(changeUnitItemFilePath, new String(changeUnitItem.getRefChangeLog().getContent()));

                            // ManagedItemList에 추가..
                            managedItemListFactory.createProduct(changeUnitItemFilePath);

                            break;
                        }
                    }
                }

                ManagedChangeUnitDTO managedChangeUnitDTO = new ManagedChangeUnitDAO().find();
                managedChangeUnitDTO.setManagedChangeUnitId(tempChangeUnit.getChangeUnitId());
                new ManagedChangeUnitDAO().update(managedChangeUnitDTO);
            }
            catch(IOException ex) {
                throw new CommandWorkerException(ex);
            }
            catch (PersistanceException ex) {
                throw new CommandWorkerException(ex);
            }
            catch(ChangeUnitException ex) {
                throw new CommandWorkerException(ex);
            }
        }
        catch(IOException ex) {
            throw new CommandWorkerException(ex);
        }
        catch(PersistanceException ex) {
            throw new CommandWorkerException(ex);
        }
        catch(ChangeUnitException ex) {
            throw new CommandWorkerException(ex);
        }
    }
}

